package com.test.utils.parallel;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.IExecutionListener;
import org.testng.ISuite;
import org.testng.ISuiteListener;
import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;

public class ParallelExecutionListener implements ITestListener, ISuiteListener, IExecutionListener {
    private static final Logger logger = LoggerFactory.getLogger(ParallelExecutionListener.class);

    private final AtomicInteger totalTests = new AtomicInteger(0);
    private final AtomicInteger passedTests = new AtomicInteger(0);
    private final AtomicInteger failedTests = new AtomicInteger(0);
    private final AtomicInteger skippedTests = new AtomicInteger(0);

    private final ConcurrentHashMap<Long, String> threadTestMap = new ConcurrentHashMap<>();

    private long suiteStartTime;
    private long suiteEndTime;

    @Override
    public void onExecutionStart() {
        logger.info("=== Parallel Test Execution Started ===");
        logger.info("Available processors: {}", Runtime.getRuntime().availableProcessors());
        logger.info("Max memory: {} MB", Runtime.getRuntime().maxMemory() / (1024 * 1024));
    }

    @Override
    public void onExecutionFinish() {
        logger.info("=== Parallel Test Execution Finished ===");
        logger.info("Total execution time: {} seconds", (suiteEndTime - suiteStartTime) / 1000);
        logger.info("Test Statistics:");
        logger.info("  Total Tests: {}", totalTests.get());
        logger.info("  Passed: {}", passedTests.get());
        logger.info("  Failed: {}", failedTests.get());
        logger.info("  Skipped: {}", skippedTests.get());
    }

    @Override
    public void onStart(ISuite suite) {
        suiteStartTime = System.currentTimeMillis();
        logger.info("Suite '{}' started with parallel mode: {}",
        suite.getName(),
        suite.getParallel());
        logger.info("Thread count: {}", suite.getXmlSuite().getThreadCount());
    }

    @Override
    public void onFinish(ISuite suite) {
        suiteEndTime = System.currentTimeMillis();
        logger.info("Suite '{}' finished. Duration: {} seconds",
        suite.getName(),
        (suiteEndTime - suiteStartTime) / 1000);
    }

    @Override
    public void onTestStart(ITestResult result) {
        totalTests.incrementAndGet();
        long threadId = Thread.currentThread().threadId();
        String testName = result.getMethod().getMethodName();

        threadTestMap.put(threadId, testName);

        logger.info("[Thread-{}] Starting test: {} in class: {}",
        threadId,
        testName,
        result.getTestClass().getName());
    }

    @Override
    public void onTestSuccess(ITestResult result) {
        passedTests.incrementAndGet();
        long threadId = Thread.currentThread().threadId();

        logger.info("[Thread-{}] Test PASSED: {} (Duration: {} ms)",
        threadId,
        result.getMethod().getMethodName(),
        result.getEndMillis() - result.getStartMillis());

        threadTestMap.remove(threadId);
    }

    @Override
    public void onTestFailure(ITestResult result) {
        failedTests.incrementAndGet();
        long threadId = Thread.currentThread().threadId();

        logger.error("[Thread-{}] Test FAILED: {} (Duration: {} ms)",
        threadId,
        result.getMethod().getMethodName(),
        result.getEndMillis() - result.getStartMillis());
        logger.error("Failure reason: ", result.getThrowable());

        threadTestMap.remove(threadId);
    }

    @Override
    public void onTestSkipped(ITestResult result) {
        skippedTests.incrementAndGet();
        long threadId = Thread.currentThread().threadId();

        logger.warn("[Thread-{}] Test SKIPPED: {}",
        threadId,
        result.getMethod().getMethodName());

        threadTestMap.remove(threadId);
    }

    @Override
    public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
        // Handle partial failures if needed
    }

    @Override
    public void onStart(ITestContext context) {
        logger.info("Test context '{}' started", context.getName());
    }

    @Override
    public void onFinish(ITestContext context) {
        logger.info("Test context '{}' finished", context.getName());

        if (!threadTestMap.isEmpty()) {
            logger.warn("Tests still running:");
            threadTestMap.forEach((threadId, testName) ->
            logger.warn("  Thread-{}: {}", threadId, testName)
            );
        }
    }

    public String getExecutionStats() {
        return String.format(
            "Execution Stats - Total: %d, Passed: %d, Failed: %d, Skipped: %d, Running: %d",
            totalTests.get(),
            passedTests.get(),
            failedTests.get(),
            skippedTests.get(),
            threadTestMap.size()
        );
    }
}
